]> dgit.raspbian.org Git - gnome-shell.git/commitdiff
Change keyboard handling API to handle nested modal calls
authorColin Walters <walters@verbum.org>
Tue, 15 Sep 2009 19:53:07 +0000 (15:53 -0400)
committerColin Walters <walters@verbum.org>
Tue, 15 Sep 2009 20:15:16 +0000 (16:15 -0400)
Rename beginModal/endModal to pushModal/popModal.  All of the current callers
just want to ensure that we're in a modal state; they don't actually need to
fail if we already are.

These functions also now take the Clutter keyboard focus, while recording
the previous focus.

https://bugzilla.gnome.org/show_bug.cgi?id=595116

js/ui/lookingGlass.js
js/ui/main.js
js/ui/overview.js
js/ui/runDialog.js

index cd83b923862db610399547408acf1d900c6bd4c5..8641782ba00d3c25b2ffe7472a4a6007fb05db30 100644 (file)
@@ -386,7 +386,7 @@ LookingGlass.prototype = {
                                          activatable: true,
                                          singleLineMode: true,
                                          text: ''});
-        /* kind of a hack */
+        /* unmapping the edit box will un-focus it, undo that */
         notebook.connect('selection', Lang.bind(this, function (nb, child) {
             if (child == this._evalBox)
                 global.stage.set_key_focus(this._entry);
@@ -548,9 +548,7 @@ LookingGlass.prototype = {
 
         Tweener.removeTweens(this.actor);
 
-        if (!Main.beginModal())
-            return;
-
+        Main.pushModal(this.actor);
         global.stage.set_key_focus(this._entry);
 
         Tweener.addTween(this.actor, { time: 0.5,
@@ -567,7 +565,7 @@ LookingGlass.prototype = {
         this._open = false;
         Tweener.removeTweens(this.actor);
 
-        Main.endModal();
+        Main.popModal(this.actor);
 
         Tweener.addTween(this.actor, { time: 0.5,
                                        transition: "easeOutQuad",
index 9df8be39b97e25325d623e5bd9472b1f1c2ea362..662e877efe6232f0e4e4cdbd45a7de547a78e145 100644 (file)
@@ -30,7 +30,8 @@ let runDialog = null;
 let lookingGlass = null;
 let wm = null;
 let recorder = null;
-let inModal = false;
+let modalCount = 0;
+let modalActorFocusStack = [];
 
 function start() {
     // Add a binding for "global" in the global JS namespace; (gjs
@@ -149,7 +150,7 @@ function _removeUnusedWorkspaces() {
 // should be asking Mutter to resolve the key into an action and then
 // base our handling based on the action.
 function _globalKeyPressHandler(actor, event) {
-    if (!inModal)
+    if (modalCount == 0)
         return false;
 
     let type = event.type();
@@ -183,27 +184,88 @@ function _globalKeyPressHandler(actor, event) {
     return false;
 }
 
-// Used to go into a mode where all keyboard and mouse input goes to
-// the stage. Returns true if we successfully grabbed the keyboard and
-// went modal, false otherwise
-function beginModal() {
+function _findModal(actor) {
+    for (let i = 0; i < modalActorFocusStack.length; i++) {
+        let [stackActor, stackFocus] = modalActorFocusStack[i];
+        if (stackActor == actor) {
+            return i;
+        }
+    }
+    return -1;
+}
+
+/**
+ * pushModal:
+ * @actor: #ClutterActor which will be given keyboard focus
+ *
+ * Ensure we are in a mode where all keyboard and mouse input goes to
+ * the stage.  Multiple calls to this function act in a stacking fashion;
+ * the effect will be undone when an equal number of popModal() invocations
+ * have been made.
+ *
+ * Next, record the current Clutter keyboard focus on a stack.  If the modal stack
+ * returns to this actor, reset the focus to the actor which was focused
+ * at the time pushModal() was invoked.
+ */
+function pushModal(actor) {
     let timestamp = global.screen.get_display().get_current_time();
 
-    if (!global.begin_modal(timestamp))
-        return false;
-    global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
+    modalCount += 1;
+    actor.connect('destroy', function() {
+        let index = _findModal(actor);
+        if (index >= 0)
+            modalActorFocusStack.splice(index, 1);
+    });
+    let curFocus = global.stage.get_key_focus();
+    if (curFocus != null) {
+        curFocus.connect('destroy', function() {
+            let index = _findModal(actor);
+            if (index >= 0)
+                modalActorFocusStack[index][1] = null;
+        });
+    }
+    modalActorFocusStack.push([actor, curFocus]);
 
-    inModal = true;
+    if (modalCount > 1)
+        return;
 
-    return true;
+    if (!global.begin_modal(timestamp)) {
+        log("pushModal: invocation of begin_modal failed");
+        return;
+    }
+    global.set_stage_input_mode(Shell.StageInputMode.FULLSCREEN);
 }
 
-function endModal() {
+/**
+ * popModal:
+ * @actor: #ClutterActor passed to original invocation of pushModal().
+ *
+ * Reverse the effect of pushModal().  If this invocation is undoing
+ * the topmost invocation, then the focus will be restored to the
+ * previous focus at the time when pushModal() was invoked.
+ */
+function popModal(actor) {
     let timestamp = global.screen.get_display().get_current_time();
 
+    modalCount -= 1;
+    let focusIndex = _findModal(actor);
+    if (focusIndex >= 0) {
+        if (focusIndex == modalActorFocusStack.length - 1) {
+            let [stackActor, stackFocus] = modalActorFocusStack[focusIndex];
+            global.stage.set_key_focus(stackFocus);
+        } else {
+            // Remove from the middle, shift the focus chain up
+            for (let i = focusIndex; i < modalActorFocusStack.length - 1; i++) {
+                modalActorFocusStack[i + 1][1] = modalActorFocusStack[i][1];
+            }
+        }
+        modalActorFocusStack.splice(focusIndex, 1);
+    }
+    if (modalCount > 0)
+        return;
+
     global.end_modal(timestamp);
     global.set_stage_input_mode(Shell.StageInputMode.NORMAL);
-    inModal = false;
 }
 
 function createLookingGlass() {
index cab7d5956a8077211a0d8e848b58aed2f7799c04..5a0b94aa5cfdf543b2bd9eff58984957a04f6f03 100644 (file)
@@ -277,8 +277,7 @@ Overview.prototype = {
     show : function() {
         if (this.visible)
             return;
-        if (!Main.beginModal())
-            return;
+        Main.pushModal(this._dash.actor);
 
         this.visible = true;
         this.animationInProgress = true;
@@ -437,7 +436,7 @@ Overview.prototype = {
 
         this._coverPane.lower_bottom();
 
-        Main.endModal();
+        Main.popModal(this._dash.actor);
         this.emit('hidden');
     },
 
index 9e3209288903c062e0d54074d9ac6afe5cb5be24..0f97cb250c0bc52fbe57e08a714f525c127aec58 100644 (file)
@@ -43,8 +43,7 @@ RunDialog.prototype = {
 
         this._internalCommands = { 'lg':
                                    Lang.bind(this, function() {
-                                       // Run in an idle to avoid recursive key grab problems
-                                       Mainloop.idle_add(function() { Main.createLookingGlass().open(); });
+                                       Main.createLookingGlass().open();
                                    }),
 
                                    'r': Lang.bind(this, function() {
@@ -182,12 +181,10 @@ RunDialog.prototype = {
         if (this._isOpen) // Already shown
             return;
 
-        if (!Main.beginModal())
-            return;
-            
         this._isOpen = true;
         this._group.show();
 
+        Main.pushModal(this._group);
         global.stage.set_key_focus(this._entry);
     },
 
@@ -203,7 +200,7 @@ RunDialog.prototype = {
         this._group.hide();
         this._entry.text = '';
 
-        Main.endModal();
+        Main.popModal(this._group);
     }
 };
 Signals.addSignalMethods(RunDialog.prototype);